﻿using HDW_Migrate.Db;
using HDW_Migrate.Db.Entities;
using HDW_Migrate.Extensions;
using HDW_Migrate.Resources;
using Microsoft.EntityFrameworkCore;
using Serilog;
using Serilog.Extensions.Logging;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Text;

namespace HDW_Migrate.Migrate;

/// <summary>
/// 数据迁移辅助类
/// </summary>
public class MigrateHelper
{
    /// <summary>
    /// 日志记录器
    /// </summary>
    public static ILogger<MigrateHelper> Logger { get; set; } = new SerilogLoggerFactory().CreateLogger<MigrateHelper>();
    /// <summary>
    /// 是否正在运行迁移
    /// </summary>
    public static bool Running { get; set; }
    /// <summary>
    /// 迁移结果
    /// </summary>
    public static MigrateResult Result { get; set; } = new MigrateResult();

    /// <summary>
    /// 获取 RIS 数据库中订单的最大和最小登记时间
    /// </summary>
    /// <returns></returns>
    public static (DateTime?, DateTime?) GetTime()
    {
        using var db = new RisDbContext();
        var max = db.Order.Max(a => a.RegisterTime);
        var min = db.Order.Min(a => a.RegisterTime);
        return (min, max);
    }

    /// <summary>
    /// 迁移
    /// </summary>
    /// <param name="param"></param>
    public static void MigrateData(MigrateParam param)
    {
        var setting = SystemSettingDto.Read();
        if (setting == null || setting.SourceDbConnection.IsEmpty() || setting.RisUrl.IsEmpty()) throw new Exception("请先完成配置");
        var time = GetTime();
        using var db = new RisDbContext();
        // 确定迁移的时间范围
        var start = param.StartDate?.Date ?? time.Item1?.Date ?? DateTime.Now.Date;
        var end = param.EndDate?.Date ?? time.Item2?.Date ?? DateTime.Now.Date;
        var curDate = start;
        var sw = new Stopwatch();
        sw.Start();
        Logger.LogInformation("------开启迁移：从 {start} 到 {end}------", start.ToDate(), end.ToDate());
        // 迁移结果初始化
        Result.StartTime = DateTime.Now;
        Result.TotalCount = 0;
        Result.Scope = (start, end);
        // 预加载必要的数据
        var criticals = db.Critical.AsNoTracking().ToList();
        var hospitals = db.Hospital.AsNoTracking().ToList();
        var patientIdentities = db.PatientIdentity.AsNoTracking().ToList();
        var chargeTypes = db.ChargeType.AsNoTracking().ToList();
        var infectionResults = db.InfectionResult.AsNoTracking().ToList();
        var hpTestResults = db.HpTestResult.AsNoTracking().ToList();
        var devices = db.Device.AsNoTracking().ToList();
        var departments = db.Department.AsNoTracking().ToList();
        var staffs = db.Staff.AsNoTracking().ToList();
        var modalities = db.Modality.AsNoTracking().ToList();
        var checkTypes = db.CheckType.AsNoTracking().ToList();
        var checkItems = db.CheckItem.AsNoTracking().ToList();
        var bodyparts = db.BodyPart.AsNoTracking().ToList();
        using var http = createHttpClient();
        http.BaseAddress = new Uri(setting.RisUrl);
        // 开始按天循环迁移
        while (true)
        {
            if (curDate > end) break;
            migrate();
            curDate = curDate.AddDays(1);
        }
        if (param.MigrateNoneTime) migrateNoneRegisterTime();      // 迁移没有登记时间的数据
        sw.Stop();
        Logger.LogInformation("------迁移完成，耗时：{second} 秒，共 {num} 条记录------", sw.ElapsedMilliseconds / 1000, Result.TotalCount);
        Result.EndTime = DateTime.Now;



        // 创建 http 客户端
        HttpClient createHttpClient()
        {
            var http = new HttpClient { BaseAddress = new Uri(setting.RisUrl) };
            var jwt = JWTUtil.GenerateToken();
            http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
            return http;
        }

        // 按日期迁移
        void migrate()
        {
            var nextDate = curDate.AddDays(1);
            var orders = db.Order.AsNoTracking().Where(a => a.RegisterTime >= curDate.Date && a.RegisterTime < nextDate).ToList();
            Log.Information("  【{date}】共 {count} 条记录", curDate.ToDate(), orders.Count);
            if (orders.Count == 0) return;
            // 按 500 条记录一批进行迁移
            var num = 0;
            while (true)
            {
                var batch = orders.Skip(500 * num).Take(500).ToList();
                if (batch.Count == 0) break;
                uploadOrders(batch);
                num++;
            }
            Logger.LogInformation("  【{date}】 迁移完成。", curDate.ToDate());
        }

        // 迁移没有登记时间的数据
        void migrateNoneRegisterTime()
        {
            var sw = new Stopwatch();
            sw.Start();
            Logger.LogInformation("  * 开始迁移没有登记时间的数据");
            var orderIds = db.Order.Where(a => a.RegisterTime == null).Select(a => a.Id).ToList();
            Logger.LogInformation("  * 无登记时间的检查记录总数：{count} 条", orderIds.Count);
            if (orderIds.Count == 0) return;
            // 按 500 条记录一批进行迁移
            var num = 0;
            while (true)
            {
                var ids = orderIds.Skip(500 * num).Take(500).ToList();
                if (ids.Count == 0) break;
                var orders = getList<Order>(ids);
                uploadOrders(orders);
                num++;
            }
            sw.Stop();
            Logger.LogInformation("  * 无登记时间的检查迁移完成，耗时：{second} 秒", sw.ElapsedMilliseconds / 1000);
        }

        // 上传订单
        void uploadOrders(List<Order> orders)
        {
            // 一次加载所有相关数据
            var orderIds = orders.Select(a => a.Id).ToList();
            var visitIds = orders.Select(a => a.VisitId).ToList();
            var visits = getList<Visit>(visitIds);
            var patientIds = visits.Select(a => a.PatientId).ToList();
            var patients = getList<Patient>(patientIds);
            var reports = getList<Report>(orderIds, "OrderId");
            var procedureList = getList<Procedure>(orderIds, "OrderId");
            var orderAttachments = getList<OrderAttachment>(orderIds, "OrderId");
            var orderScheduleLogs = getList<OrderScheduleLog>(orderIds, "OrderId").OrderByDescending(a => a.CreateDateTime).ToList();
            var reportEventIds = reports.SelectMany(a =>
            {
                var arr = new List<Guid>();
                if (a.ApproveEvent_Id.HasValue) arr.Add(a.ApproveEvent_Id.Value);
                if (a.SubmitEvent_Id.HasValue) arr.Add(a.SubmitEvent_Id.Value);
                return arr;
            }).ToList();
            var reportEvents = getList<ReportEvent>(reportEventIds);

            // 逐条上传
            orders.ForEach(order =>
            {
                var visit = visits.FirstOrDefault(a => a.Id == order.VisitId);
                if (visit == null) return;
                var patient = patients.FirstOrDefault(a => a.Id == visit?.PatientId);
                if (patient == null) return;
                var report = reports.FirstOrDefault(a => a.OrderId == order.Id);
                var reportSubmitEvent = reportEvents.FirstOrDefault(a => a.Id == report?.SubmitEvent_Id);
                var reportApproveEvent = reportEvents.FirstOrDefault(a => a.Id == report?.ApproveEvent_Id);
                // 检查项目
                var procedures = procedureList.Where(a => a.OrderId == order.Id).Select(a =>
                {
                    var item = new ProcedureEx
                    {
                        Procedure = a,
                        CheckItem = checkItems.FirstOrDefault(b => b.Id == a.CheckItemId),
                        BodyPart = bodyparts.FirstOrDefault(b => b.Id == a.BodyPartId)
                    };
                    item.CheckType = checkTypes.FirstOrDefault(b => b.Id == item.CheckItem?.CheckTypeId);
                    return item;
                }).ToList();
                // 预约日志
                var orderScheduleLog = orderScheduleLogs.FirstOrDefault(a => a.OrderId == order.Id);
                // 检查医生
                var execDoctor = staffs.FirstOrDefault(a => a.Id == order.ExecDoctorId);
                execDoctor ??= staffs.FirstOrDefault(a => a.Id == procedures.FirstOrDefault()?.Procedure.PerformStaffId);
                var data = new IntegratedData
                {
                    Order = order,
                    Report = report,
                    Visit = visit,
                    Procedures = procedures,
                    Patient = patient,
                    Critical = criticals.FirstOrDefault(a => a.Id == visit.CriticalId),
                    Hospital = hospitals.FirstOrDefault(a => a.Id == patient.HospitalId),
                    Identity = patientIdentities.FirstOrDefault(a => a.Id == visit.PatientIdentityId),
                    ChargeType = chargeTypes.FirstOrDefault(a => a.Id == visit.ChargeTypeId),
                    InfectionResult = infectionResults.FirstOrDefault(a => a.Id == visit.InfectionResultId),
                    HpTestResult = hpTestResults.FirstOrDefault(a => a.Id == visit.HpTestResultId),
                    Device = devices.FirstOrDefault(a => a.Id == order.DeviceId),
                    ExecDepartment = departments.FirstOrDefault(a => a.Id == order.ExecDepartmentId),
                    ExecDoctor = execDoctor,
                    Modality = modalities.FirstOrDefault(a => a.Id == order.ModalityId),
                    ApplyDoctor = staffs.FirstOrDefault(a => a.Id == order.ApplyDoctorId),
                    ApplyDepartment = departments.FirstOrDefault(a => a.Id == order.OrderDepartmentId),
                    CheckInDoctor = staffs.FirstOrDefault(a => a.Id == order.CheckInDoctorId),
                    ApproveDoctor = staffs.FirstOrDefault(a => a.Id == reportApproveEvent?.OperatorId),
                    SubmitDoctor = staffs.FirstOrDefault(a => a.Id == reportSubmitEvent?.OperatorId),
                    ImageReader = staffs.FirstOrDefault(a => a.Id == report?.ImageReaderId),
                    OrderAttachments = orderAttachments,
                    OrderScheduleLog = orderScheduleLog,
                    ScheduleDoctor = orderScheduleLog == null ? null : staffs.FirstOrDefault(a => a.Id == orderScheduleLog?.StaffId),
                    RegionHospital = hospitals.FirstOrDefault(a => a.ServerNode == order.RegionAssistNode),
                    PdfBase64 = getPdf(report, order),
                };
                var archive = data.ParseArchiveView();
                uploadArchive(archive);
            });
            Result.TotalCount += orders.Count;
        }

        // 上传档案
        void uploadArchive(ArchiveUploadPara archive)
        {
            var body = new StringContent(archive.ToJson(), Encoding.UTF8, "application/json");
            var response = http.PostAsync("/api/Archive/SaveWithPdfReport", body);
            try
            {
                response.GetAwaiter().GetResult().EnsureSuccessStatusCode();
            }
            catch (Exception e)
            {
                Logger.LogInformation("  **** 文档上传错误：{accessionNumber}, {error}", archive.Data.Order.AccessionNumber, e.Message);
            }
        }

        // 获取报告 PDF ，返回 Base64 字符串
        string getPdf(Report report, Order order)
        {
            if (report == null || report.SnapshotFilePath.IsEmpty()) return null;
            try
            {
                return File.ReadAllBytes(report.SnapshotFilePath).ToBase64();
            }
            catch (Exception e)
            {
                Logger.LogInformation("  **** 读取报告 PDF 文档错误：{accessionNumber}, {error}", order.AccessionNumber, e.Message);
            }
            return null;
        }

        // 获取符合条件的实体列表，使用 linq 查询时，会出现莫名的问题 【db.Visit.Where(a => visitIds.Contains(a.Id)).ToList()】
        List<T> getList<T>(List<Guid> guids, string key = "Id") where T : class
        {
            if (guids.Count == 0) return [];
            var arr = guids.Select(a => $"'{a}'");
            var type = typeof(T);
            var parameters = string.Join(",", arr);
            var sql = $@"SELECT * FROM [{type.Name}] WHERE {key} IN ({parameters})";
            var list = db.Set<T>().FromSqlRaw(sql).AsNoTracking().ToList();
            return list ?? [];
        }

    }

}

/// <summary>
/// 迁移结果
/// </summary>
public class MigrateResult
{
    /// <summary>
    /// 开始迁移时间
    /// </summary>
    public DateTime? StartTime { get; set; }
    /// <summary>
    /// 截止迁移时间
    /// </summary>
    public DateTime? EndTime { get; set; }
    /// <summary>
    /// 迁移总数
    /// </summary>
    public int TotalCount { get; set; }
    /// <summary>
    /// 迁移的时间范围
    /// </summary>
    public (DateTime, DateTime) Scope { get; set; }
    /// <summary>
    /// 提示语
    /// </summary>
    public string Msg { get; set; }
}
